/////////////////////////////////////////////////////////////////////////////////////////////////////
// 
// NHP协议的钉钉小程序JS SDK
// 版本号：v3.10 (发布于2024-06-12）
// 关于NHP协议的定义，请参见中国计算机学会CCF的《零信任网络隐身协议规范》
// 反馈意见请发送至： zsec@z-sec.net，感谢您的参与支持！
//
/////////////////////////////////////////////////////////////////////////////////////////////////////

import dd from "gdt-jsapi";

////////////////////////////////////////////////////////////////////////////
// 常量定义
////////////////////////////////////////////////////////////////////////////

// 全局开关
var DEBUG_MODE = 0; // 是否开启Debug模式，发布时应该为0

// 版本号
const NHP_VERSION = "3.10";

// 错误码定义
const ERR_SUCCESS = 0;        // 成功
const ERR_EXIT    = 1;        // 立即退出
const ERR_HTTPREQ = -1;       // 对服务器的HTTP请求失败
const ERR_INVALIDPARAM = -2;  // 错误的函数参数
const MSG_INVALIDPARAM = '[NHP] Invalid Parameters'; //ERR_INVALIDPARAM对应的错误消息
const ERR_ASPTOKEN = -3;      // 获取钉钉的Token失败
const MSG_ASPTOKEN = '[NHP] Failed to get AuthCode';
const ERR_NOTSUPPORTED = -4;  // 系统不支持DingDing的API
const MSG_NOTSUPPORTED = '[NHP] DingTalk API not supported';

////////////////////////////////////////////////////////////////////////////
// 内部函数
////////////////////////////////////////////////////////////////////////////

// Print debug log
function debugLog(msg) {
  if (DEBUG_MODE) { // 调试模式下，才打印日志
    console.info(msg);
  }
}

////////////////////////////////////////////////////////////////////////////
// 敲门的内部实现函数，不对外暴露，请见下方的对外暴露函数 open() 和 close() 
////////////////////////////////////////////////////////////////////////////
function nhpKnockDoor(nhpServer, aspId, resId, callback) {
  debugLog("nhp.nhpKnockDoor: nhpServer=" + nhpServer + "; aspId=" + aspId + "; resId=" + resId);
  // check parameters
  if (!nhpServer || !aspId || !resId || !callback) {
    debugLog(MSG_INVALIDPARAM);
    callback({
      errCode: ERR_INVALIDPARAM, // 输入参数错误，程序中断
      errMsg:  MSG_INVALIDPARAM
    });
    return;
  }

  // check API support
  if (!dd || !dd.getAuthCode || (!dd.httpRequest && !(window && window.fetch))) {
    debugLog(MSG_NOTSUPPORTED);
    callback({
      errCode: ERR_NOTSUPPORTED, // 输入参数错误，程序中断
      errMsg:  MSG_NOTSUPPORTED
    });
    return;
  }
  
  // 获取钉钉的客户端Token，用于向钉钉的身份服务器验证用户身份
  dd.getAuthCode().then((authCodeResult) =>{
    debugLog(authCodeResult);
    if (authCodeResult) {
      const aspToken = authCodeResult.code; /*钉钉的客户端Token*/
      const nhpURL = "https://" + nhpServer + 
            "/plugins/" + encodeURIComponent(aspId) + 
            "?resid=" + encodeURIComponent(resId) + 
            "&token=" + encodeURIComponent(aspToken) + 
            "&action=valid";
      debugLog("NHP URL: " + nhpURL);
      
      // 向NHP服务器发起AJAX请求
      if (dd.httpRequest) { // 通过钉钉内置的dd.httpRequest函数发起请求
        dd.httpRequest({
          url: nhpURL, // NHP服务器请求URL
          method: "GET", // HTTP GET
          headers: {
            "X-NHP-Ver": NHP_VERSION, // NHP SDK版本号
            "Content-Type": "application/json" // JSON格式
          },
          dataType: "json",
        }).then(nhpResult => {
          debugLog(nhpResult);
          // 判断是否HTTP请求成功（200）
          if (nhpResult.status != 200) {
            throw new Error(`HTTP error: ${nhpResult.status}`);
          } else {
            // 根据文档:https://open-portal.on-premises.dingtalk.com/portal/#/helpdoc?apiType=jsapi&docKey=2675265
            // data字段是返回的数据
            return nhpResult.data;
          }
        }).then((nhpJson) => {
          debugLog(nhpJson);
          // Assign the aspToken field if server didn't give back
          if (!nhpJson.aspToken) {
              nhpJson.aspToken = aspToken;
          }
          // 通过回调函数返回
          callback(nhpJson);
        }).catch(err => {
          // 对NHP服务器的HTTP请求失败，需要提示调用者
          callback({
            errCode: ERR_HTTPREQ, // 对NHP服务器的HTTP请求失败为-2；
            errMsg: err.message
          });  
        });
      } else if (window && window.fetch) { // 通过标准JS的fetch()函数发起请求
        window.fetch(nhpURL, {
          method: "GET", // HTTP GET
          headers: {
            "X-NHP-Ver": NHP_VERSION, // NHP SDK版本号
            "Content-Type": "application/json" // JSON格式
          },
          dataType: "json",
        }).then(nhpResult => {
          debugLog(nhpResult);
          // 判断是否HTTP请求成功（200）
          if (!nhpResult.ok) {
            throw new Error(`HTTP error: ${nhpResult.status}`);
          }
          // fetch() 的结果进行JSON化
          return nhpResult.json();
        }).then((nhpJson) => {                   
          debugLog(nhpJson);
          // Assign the aspToken field if server didn't give back
          if (!nhpJson.aspToken) {
            nhpJson.aspToken = aspToken;
          }
          // 通过回调函数返回
          callback(nhpJson);
        }).catch(err => {
          // 对NHP服务器的HTTP请求失败，需要提示调用者
          callback({
            errCode: ERR_HTTPREQ, // 对NHP服务器的HTTP请求失败为-2；
            errMsg: err.message
          });  
        });
      } else {
        debugLog("[NHP Error] NO AJAX API supported!");
      }
    } else {//if (authCodeResult) 
      // 获取钉钉的Token失败，需要提示调用者
      callback({
        errCode: ERR_ASPTOKEN, // 获取钉钉的Token失败的错误
        errMsg: MSG_ASPTOKEN
      });
    }
  }); //dd.getAuthCode().then((authCodeResult) =>{
} //function open(nhpServer, aspId, resId, interval, callback)


/**
 * debug模式开关函数
 *
 * @param {number} debugMode 0：关闭debug模式；1：打开debug模式；
 *
 */
function debug(debugMode) {
  DEBUG_MODE = debugMode;
} // function debug

/**
 * NHP协议开启敲门函数，调用open函数将开启以interval间隔为时长的连续敲门。
 *
 * @param {number} interval 敲门间隔时长，以毫秒ms为单位，例如20*1000 表示20秒。
 * @param {string} nhpServer NHP服务器地址，可以为域名或者IP地址，例如"nhp.huzhou.gov.cn"。
 * @param {string} aspId 身份授权服务提供商ASP的ID，例如 "zzd"表示浙政钉、"zlb"表示浙里办。
 * @param {string} resId 目标资源的ID，一般为浙政钉的AppId，如"2001832595"
 * @param {function} callback 回调函数，callback(msg)，msg为NHP服务器返回的消息JSON。
 * @return {number} NHP的敲门计时器ID，即timerID，可以通过调用close(timerID)来关闭敲门。
 */
function open(interval, nhpServer, aspId, resId, callback) {
  debugLog("nhp.open: interval=" + interval + ";nhpServer=" + nhpServer + "; aspId=" + aspId + "; resId=" + resId);
  
  // 马上执行一次敲门函数
  nhpKnockDoor(
    nhpServer,    // NHP 服务器地址，例如"nhp.huzhou.gov.cn"
    aspId,        // 授权服务提供商ASP的ID，，例如 "zzd"表示浙政钉、"zlb"表示浙里办
    resId,        // 目标资源的ID，一般为浙政钉的AppId，如"2001832595"
    callback      // 回调函数，callback(msg)，msg为NHP服务器返回的消息
  );

   // 每隔interval秒执行一次 敲门函数
  const timerID = setInterval(
    function() {
      nhpKnockDoor(nhpServer, aspId, resId, callback); // 敲门函数
    },
    interval      // 敲门间隔
  );
  return timerID;
} // function open(nhpServer, aspId, resId, interval, callback)

/**
 * NHP协议关闭敲门函数。
 *
 * @param {number} timerID NHP的敲门计时器ID。
 */
function close(timerID) {
  if (timerID) {
    clearInterval(timerID);
  }
} //function close(timerID) 

/**
 * NHP协议开启敲门函数open的Promise实现。
 *
 * @param {number} interval 敲门间隔时长，以毫秒ms为单位，例如20*1000 表示20秒。
 * @param {string} nhpServer NHP服务器地址，可以为域名或者IP地址，例如"nhp.huzhou.gov.cn"。
 * @param {string} aspId 身份授权服务提供商ASP的ID，例如 "zzd"表示浙政钉、"zlb"表示浙里办。
 * @param {string} resId 目标资源的ID，一般为浙政钉的AppId，如"2001832595"
 * @return {Promise} 登录结果的Promise对象。
 */
function openPromise(interval, nhpServer, aspId, resId) {
  debugLog(
    "[NHP] openPromise: interval=" + interval + 
    ";nhpServer=" + nhpServer + 
    "; aspId=" + aspId + 
    "; resId=" + resId
  );

  return new Promise((resolve, reject) => {
    open(interval, nhpServer, aspId, resId, function(msg) {
      if (msg.errCode == ERR_SUCCESS) {
        resolve(msg);
      } else {
        reject(msg);
      }
    });
  });
}

////////////////////////////////////////////////////////////////////////////
// 本JS模块对外暴露的函数和常量
////////////////////////////////////////////////////////////////////////////
export default {
  open: open, // 开启敲门
  openPromise : openPromise, // 开启敲门的Promise实现
  close: close, // 关闭敲门
  debug: debug,   // 开启/关闭Debug模式
  ERR_CODE: { // 错误码常量
    success: ERR_SUCCESS,
    exit: ERR_EXIT,
    httpReq: ERR_HTTPREQ,
    invalidParam: ERR_INVALIDPARAM,
    aspToken: ERR_ASPTOKEN,
    notSupported: ERR_NOTSUPPORTED
  }
};
